/**
 * Copyright [2019] [LiBo/Alex of copyright liboware@gmail.com ]
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hyts.dess.base.shutdown;

import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.Connector;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.util.Assert;

import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @project-name:dess
 * @package-name:com.hyts.dess.base.shutdown
 * @author:LiBo/Alex
 * @create-date:2020-05-27 17:12
 * @copyright:libo-alex4java
 * @email:liboware@gmail.com
 * @description:
 * 要平滑关闭 Spring Boot 应用的前提就是首先要关闭其内置的 Web 容器，不再处理外部新进入的请求。为了能让应用接受关闭事件通知的时候，保证当前 Tomcat 处理所有已经进入的请求，
 * 我们需要实现 TomcatConnectorCustomizer 接口，这个接口的源码十分简单，从注释可以看出这是实现自定义 Tomcat Connector 行为的回调接口：
 */
@Slf4j
public class CustomShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {

    /**
     * 连接器操作对象
     */
    private volatile Connector connector;

    /**
     * 关闭连接对象的最大超时时间
     */
    private static final int SHUTDOWN_CONNECTOR_MAX_TIME_SECONDS = 30;

    /**
     * 赋值操作
     * @param connector
     */
    @Override
    public void customize(org.apache.catalina.connector.Connector connector) {
        this.connector = connector;
    }

    /**
     * 监听关闭的系统服务钩子监听器
     * @param contextClosedEvent
     */
    @Override
    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
        Assert.notNull(connector, "无法获取相关的connector连接器操作");
        connector.pause();
        log.info("暂停相关连接器");
        Executor executor = this.connector.getProtocolHandler().getExecutor();
        log.info("获取协议处理器中的其中处理请求消息数据的连接池");
        if (executor instanceof ThreadPoolExecutor) {
            log.info("进行相关ThreadPoolExecutor连接池的关闭操作");
            ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
            threadPoolExecutor.shutdown();
            log.info("ThreadPoolExecutor关闭完成");
            try {
                boolean result = threadPoolExecutor.awaitTermination(SHUTDOWN_CONNECTOR_MAX_TIME_SECONDS, TimeUnit.SECONDS);
                if(result){
                    log.info("WEB 应用关闭成功");
                }else{
                    log.error("WEB 应用关闭失败!");
                }
            } catch (InterruptedException e) {
                log.error("关闭线程中断，无法关闭系统服务进程！",e);
                Thread.currentThread().interrupt();
            }
        }
    }
}
